package org.bedracket.pgeg.init;

import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DropExperienceBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Material;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.bedracket.pgeg.PGEGMod;
import org.bedracket.pgeg.block.TomatoCrop;
import org.jetbrains.annotations.Nullable;

import java.util.Locale;
import java.util.function.Function;
import java.util.function.Supplier;

public class ModBlocks {

    public static final DeferredRegister<Block> BLOCKS =
            DeferredRegister.create(ForgeRegistries.BLOCKS, PGEGMod.MOD_ID);

    public static final RegistryObject<Block> TOMATO =
            register("tomato", TomatoCrop::new);

    public static final RegistryObject<Block> AIR_INFUSED_STONE =
            registerDefault("air_infused_stone", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> AQUA_INFUSED_STONE =
            registerDefault("aqua_infused_stone", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> EARTH_INFUSED_STONE =
            registerDefault("earth_infused_stone", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> ENTROPY_INFUSED_STONE =
            registerDefault("entropy_infused_stone", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> FIRE_INFUSED_STONE =
            registerDefault("fire_infused_stone", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> ORDER_INFUSED_STONE =
            registerDefault("order_infused_stone", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> AIR_INFUSED_DEEPSLATE =
            registerDefault("air_infused_deepslate", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> AQUA_INFUSED_DEEPSLATE =
            registerDefault("aqua_infused_deepslate", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> EARTH_INFUSED_DEEPSLATE =
            registerDefault("earth_infused_deepslate", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> ENTROPY_INFUSED_DEEPSLATE =
            registerDefault("entropy_infused_deepslate", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> FIRE_INFUSED_DEEPSLATE =
            registerDefault("fire_infused_deepslate", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> ORDER_INFUSED_DEEPSLATE =
            registerDefault("order_infused_deepslate", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> BRIMSTONE_ORE =
            registerDefault("brimstone_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> METEORITE_ORE =
            registerDefault("meteorite_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> MITHRIL_ORE =
            registerDefault("mithril_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> ORICHALCUM_ORE =
            registerDefault("orichalcum_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> SATURNIUM_ORE =
            registerDefault("saturnium_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> SILVER_ORE =
            registerDefault("silver_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> DEEPSLATE_BRIMSTONE_ORE =
            registerDefault("deepslate_brimstone_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> DEEPSLATE_METEORITE_ORE =
            registerDefault("deepslate_meteorite_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> DEEPSLATE_MITHRIL_ORE =
            registerDefault("deepslate_mithril_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> DEEPSLATE_ORICHALCUM_ORE =
            registerDefault("deepslate_orichalcum_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> DEEPSLATE_SATURNIUM_ORE =
            registerDefault("deepslate_saturnium_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    public static final RegistryObject<Block> DEEPSLATE_SILVER_ORE =
            registerDefault("deepslate_silver_ore", () ->
                    new DropExperienceBlock(BlockBehaviour
                            .Properties.of(Material.STONE)
                            .sound(SoundType.DEEPSLATE)
                            .requiresCorrectToolForDrops().strength(3.0F,
                                    3.0F)));

    /**
     * Hanlde Default Register
     * @param name registry name
     * @param blockSupplier blocks
     * @param blockItemFactory items
     * @return new Instance
     * @param <T> sth extends Block
     */
    private static <T extends Block> RegistryObject<T> register(String name, Supplier<T> blockSupplier, @Nullable Function<T, ? extends BlockItem> blockItemFactory) {
        return register(ModBlocks.BLOCKS, ModItems.ITEMS, name, blockSupplier, blockItemFactory);
    }

    /**
     * Handle the default registry block
     * @param name registry name
     * @param blockSupplier blocks
     * @return new Instance
     * @param <T> sth extends Block
     */
    private static <T extends Block> RegistryObject<T> registerDefault(String name, Supplier<T> blockSupplier) {
        return register(name, blockSupplier, block -> new BlockItem(block, new Item.Properties()));
    }

    /**
     * used for registry default BlockItems
     * @param blocks Blocks' Instance
     * @param items Items' Instance
     * @param name BlockItems' registry name
     * @param blockSupplier supplier with Blocks
     * @param blockItemFactory ItemProperties
     * @return new RegistryObject<Block>
     * @param <T> sth extends Block
     */
    public static <T extends Block> RegistryObject<T> register(DeferredRegister<Block> blocks, DeferredRegister<Item> items, String name, Supplier<T> blockSupplier, @Nullable Function<T, ? extends BlockItem> blockItemFactory) {
        final String actualName = name.toLowerCase(Locale.ROOT);
        final RegistryObject<T> block = blocks.register(actualName, blockSupplier);
        if (blockItemFactory != null) {
            items.register(actualName, () -> blockItemFactory.apply(block.get()));
        }
        return block;
    }

    /**
     * Used for registry Block
     * @param name Blocks' registry name
     * @param block Block Instance
     * @return new RegistryObject<Block>
     * @param <T> sth extends Block
     */
    private static <T extends Block> RegistryObject<T> register(String name, Supplier<T> block) {
        return BLOCKS.register(name.toLowerCase(Locale.ROOT), block);
    }
}
